package com.nowcoder.community.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.CharUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class SensitiveWordFilter {

    private final TrieNode root;
    private static final String REPLACEMENT = "**";

    // 构造前缀树
    public SensitiveWordFilter() {
        root = new TrieNode();
        try (
                InputStream inputStream = new ClassPathResource("sensitiveWords.txt").getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        ){
            String word;
            while ((word = bufferedReader.readLine()) != null)
                addWord(word);
        } catch (IOException e) {
            log.error("读取敏感词文件失败");
        }
    }

    // 向前缀树添加敏感词
    private void addWord(String word){
        int N = word.length();
        TrieNode point = root;
        for (int i = 0; i < N; i++){
            Character c = word.charAt(i);
            point.addSubNode(c);
            point = point.getSubNode(c);
        }
        point.setEndOfWord(true);
    }

    // 过滤敏感词算法实现
    // begin 指向词的开始，end 指向当前和前缀树比对的字字符，比对结果有以下几种可能
    //  1.前缀树节点为 null，说明不是敏感词；将文本的begin位置的字符加入结果 并且 begin + 1
    //  2.前缀树节点不为 null 且 节点的 endOfWord 为 false ，说明可能是敏感词，将 end++
    //  3.前缀树节点不为 null 且 节点的 endOfWord 为 True，说明找到一个敏感词，将敏感词替换并且将 begin 设置为 end + 1
    public String filter(String text){
        StringBuilder result = new StringBuilder();
        int N = text.length();

        for (int begin = 0; begin < N; begin++){
            TrieNode point = root;
            for (int end = begin; end < N; end++){
                char c = text.charAt(end);
                if (begin != end && isSymbol(c))
                    continue;

                point = point.getSubNode(c);
                if (point == null){
                    // 不是敏感词
                    result.append(text.charAt(begin));
                    break;
                }else if (point.isEndOfWord()){
                    // 是敏感词
                    result.append(REPLACEMENT);
                    begin = end;
                    break;
                }
            }
        }

        return result.toString();
    }

    // 判断是否是特殊符号
    private boolean isSymbol(char c){
        // 0x2E80 ~ 0x9FFF 为东亚字符范围
        return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }

    // 前缀树节点
    private class TrieNode{
        private boolean endOfWord;
        private Map<Character, TrieNode> subNode;

        public TrieNode() {
            endOfWord = false;
            subNode = new HashMap<>();
        }

        public boolean isEndOfWord() {
            return endOfWord;
        }

        public void setEndOfWord(boolean endOfWord) {
            this.endOfWord = endOfWord;
        }

        public TrieNode getSubNode(Character c) {
            return subNode.get(c);
        }

        public void addSubNode(Character c) {
            if (!subNode.containsKey(c))
                subNode.put(c,  new TrieNode());
        }
    }
}
